// -----------------------------------------------------------------------
// <copyright file="Level.cs" company="Diddly Squad">
// </copyright>
// -----------------------------------------------------------------------

using System.Collections.Generic;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using System.IO;
using DiddlySquatDataTypes;
using System.Xml.Serialization;
using Microsoft.Xna.Framework;

namespace DiddlySquat
{
	using System;
	using System.Linq;

	/// <summary>
	/// Class to represent level of the DiddlySquat game
	/// </summary>
	public class Level
	{
		public List<Obstacle> ObstacleList { get; set; }

		public SaveGame SaveGame { get; set; }

		private readonly ContentManager content;

		private readonly World world;

		/// <summary>
		/// Initializes a new instance of the <see cref="Level"/> class.
		/// </summary>
		public Level() {}

		/// <summary>
		/// Initializes a new instance of the <see cref="Level"/> class.
		/// </summary>
		/// <param name="contentManager">The content manager.</param>
		/// <param name="world">The world.</param>
		public Level(ContentManager contentManager, World world)
		{
			this.content = contentManager;
			this.world = world;

		}

		/// <summary>
		/// Creates the example level.
		/// </summary>
		public void CreateExampleLevel()
		{
			this.ObstacleList = new List<Obstacle>();

			int listIndex = 0;
			for (int i = 0; i < 3; i++)
			{
				var wallSprite = content.Load<Texture2D>(Constants.ObstacleSprite);
				wallSprite.Name = Constants.ObstacleSprite;
				this.ObstacleList.Add(new Obstacle());
				this.ObstacleList[listIndex].Initialize(world, 0, 8 + i, 0, 0, wallSprite, Enums.ObstacleType.BasicObstacle);
				listIndex++;
			}

			for (int i = 0; i < 10; i++)
			{
				var wallSprite = content.Load<Texture2D>(Constants.ObstacleSprite);
				wallSprite.Name = Constants.ObstacleSprite;
				this.ObstacleList.Add(new Obstacle());
				this.ObstacleList[listIndex].Initialize(world, 13, 10 - i, 0, 0, wallSprite, Enums.ObstacleType.BasicObstacle);
				listIndex++;
			}

			for (int i = 0; i < 10; i++)
			{
				var wallSprite = content.Load<Texture2D>(Constants.ObstacleSprite);
				wallSprite.Name = Constants.ObstacleSprite;
				this.ObstacleList.Add(new Obstacle());
				this.ObstacleList[listIndex].Initialize(world, 10, 5 - i, 0, 0, wallSprite, Enums.ObstacleType.BasicObstacle);
				listIndex++;
			}

			for (int i = 0; i < 2; i++)
			{
				var wallSprite = content.Load<Texture2D>(Constants.ObstacleSprite2);
				wallSprite.Name = Constants.ObstacleSprite2;
				this.ObstacleList.Add(new Obstacle());
				this.ObstacleList[listIndex].Initialize(
					world, 12f + 2f * i, 9, 0, 1 * (-1 + 2 * i), wallSprite, Enums.ObstacleType.BasicObstacle);
				listIndex++;
			}

			// Create the ground
			for (int i = -10; i < 15; i++)
			{
				var groundSprite = content.Load<Texture2D>(Constants.ObstacleSprite3);
				groundSprite.Name = Constants.ObstacleSprite3;
				this.ObstacleList.Add(new Obstacle());
				this.ObstacleList[listIndex].Initialize(
					world, i * 450 / Constants.MeterInPixels, 10, 1, 0f, groundSprite, Enums.ObstacleType.BasicObstacle);
				listIndex++;
			}

			var startSprite = content.Load<Texture2D>(Constants.StartSprite);
			startSprite.Name = Constants.StartSprite;
			this.ObstacleList.Add(new Obstacle());
			this.ObstacleList[listIndex].Initialize(
				world, 1 * 450 / Constants.MeterInPixels, 9, 1, 0f, startSprite, Enums.ObstacleType.Start);
			listIndex++;

			var exitSprite = content.Load<Texture2D>(Constants.ExitSprite);
			exitSprite.Name = Constants.ExitSprite;
			this.ObstacleList.Add(new Obstacle());
			this.ObstacleList[listIndex].Initialize(
				world, -1 * 450 / Constants.MeterInPixels, 9, 1, 0f, exitSprite, Enums.ObstacleType.Finish);
			listIndex++;

			var collectableSprite = content.Load<Texture2D>(Constants.ObstacleSprite6);
			exitSprite.Name = Constants.ObstacleSprite6;
			this.ObstacleList.Add(new Collectable());
			this.ObstacleList[listIndex].Initialize(
				world, 0 * 450 / Constants.MeterInPixels, 8, 1, 0f, collectableSprite, Enums.ObstacleType.Collectable);

		}

		/// <summary>
		/// Creates the obstacles.
		/// </summary>
		/// <param name="savedObstacles">The saved obstacles.</param>
		public void CreateObstacles(List<SavedObstacle> savedObstacles)
		{
			ObstacleList = new List<Obstacle>();

			foreach (SavedObstacle savedObstacle in savedObstacles)
			{
				Obstacle obstacle;
				if ((Enums.ObstacleType)savedObstacle.ObstacleType == Enums.ObstacleType.Finish)
					obstacle = new Exit();
				else
					obstacle = new Obstacle();

				var obstacleSprite = content.Load<Texture2D>(savedObstacle.ImageName);
				obstacleSprite.Name = savedObstacle.ImageName;
				obstacle.Initialize(
					this.world,
					savedObstacle.XPos,
					savedObstacle.YPos,
					1,
					savedObstacle.Angle,
					obstacleSprite,
					(Enums.ObstacleType)(savedObstacle.ObstacleType));
				ObstacleList.Add(obstacle);
			}
		}


		///// <summary>
		///// Generates template data for levels. Not used in actual game.
		///// </summary>
		///// <param name="saveGame">The save game.</param>
		//public void Save(SaveGame saveGame)
		//{
		//    List<SavedObstacle> list = new List<SavedObstacle>();

		//    foreach (Obstacle obstacle in this.ObstacleList)
		//    {
		//        SavedObstacle savedObstacle = new SavedObstacle(obstacle.ObstacleBody.Position.X, obstacle.ObstacleBody.Position.Y, obstacle.ObstacleBody.Rotation, obstacle.ObstacleSprite.Name, (int)obstacle.ObstacleType);
		//        list.Add(savedObstacle);
		//    }

		//    LevelData levelData = new LevelData(list, saveGame);

		//    IAsyncResult res = StorageDevice.BeginShowSelector(null, null);
		//    if (res.IsCompleted)
		//    {
		//        StorageDevice device = StorageDevice.EndShowSelector(res);

		//        // Open a storage container.
		//        IAsyncResult result = device.BeginOpenContainer("DiddlySquat", null, null);

		//        // Wait for the WaitHandle to become signaled.
		//        result.AsyncWaitHandle.WaitOne();

		//        StorageContainer container = device.EndOpenContainer(result);

		//        // Close the wait handle.
		//        result.AsyncWaitHandle.Close();

		//        string filename = "example.xml";

		//        // Check to see whether the save exists.
		//        if (container.FileExists(filename)) // Delete it so that we can create one fresh.
		//            container.DeleteFile(filename);

		//        // Create the file.
		//        Stream stream = container.CreateFile(filename);

		//        // Convert the object to XML data and put it in the stream.
		//        XmlSerializer serializer = new XmlSerializer(typeof(LevelData));

		//        serializer.Serialize(stream, levelData);

		//        // Close the file.
		//        stream.Close();
		//        // Dispose the container, to commit changes.
		//        container.Dispose();
		//    }
		//}

		/// <summary>
		/// Loads the specified level data.
		/// </summary>
		/// <param name="levelData">The level data.</param>
		public void Load(LevelData levelData)
		{
			//this.ClearObstacles();
			this.CreateObstacles(levelData.SavedObstacles);
			this.SaveGame = levelData.SaveGame;
			
		}

		/// <summary>
		/// Loads the level.
		/// </summary>
		/// <param name="filename">The filename.</param>
		/// <returns>LevelData of level elements</returns>
		public LevelData LoadLevel(string filename)
		{
			Stream myStream =
				TitleContainer.OpenStream(content.RootDirectory + "\\" + Constants.LevelsFolder + "\\" + filename);

			XmlSerializer serializer = new XmlSerializer(typeof(LevelData));
			LevelData data = (LevelData)serializer.Deserialize(myStream);
			myStream.Close();
			return data;
			
		}

		/// <summary>
		/// Gets the levels from file system.
		/// </summary>
		/// <returns>List of level datas</returns>
		public List<LevelData> GetLevels()
		{
			DirectoryInfo dir = new DirectoryInfo(content.RootDirectory + "\\" + Constants.LevelsFolder);
			if (!dir.Exists)
				throw new DirectoryNotFoundException();
			
			//Init the resulting list
			var result = new List<LevelData>();

			//Load all files that matches the file filter
			//Sorts as default by filename witch is really good for us :)
			FileSystemInfo[] files = dir.GetFileSystemInfos("*.xml");
			var orderedFiles = files.OrderBy(f => f.Name);
			foreach (FileSystemInfo file in orderedFiles)
			{
				result.Add(this.LoadLevel(file.Name));
			}

			return result;
		}

		/// <summary>
		/// Clears the obstacles.
		/// </summary>
		private void ClearObstacles()
		{
			if (ObstacleList != null)
			{
				for (int i = ObstacleList.Count - 1; i >= 0; i--)
				{
					ObstacleList[i].Delete(this.world);
				}
			}
		}
	}

}